﻿//-----------------------------------------------------------------------
// <copyright file="KinectHands.cs" company="xBrainLab">
//     Copyright (c) xBrainLab. All rights reserved.
// </copyright>
// <author>Sebastien Warin</author>
//-----------------------------------------------------------------------
namespace KinectTouchDevice
{
    using System;
    using System.Threading;
    using NITE;
    using OpenNI;

    public class KinectHands
    {
        private readonly string SAMPLE_XML_FILE = "Tracking.xml"; //@"C:\Program Files (x86)\PrimeSense\NITE\Data\Sample-Tracking.xml";

        public event EventHandler<HandPointEventArgs> HandPointCreate;
        public event EventHandler<HandPointEventArgs> HandPointUpdated;
        public event EventHandler<IdEventArgs> HandPointDetroy;

        private Context context;
        private SessionManager sessionManager;
        private WaveDetector waveDetector;
        private FlowRouter flowRouter;
        private PointDenoiser pointDenoiser;

        private bool isRunning = false;

        public int Height { get; set; }
        public int Width { get; set; }

        private Thread readerThread;

        public KinectHands() : this(640, 480) { }
        public KinectHands(int width, int height)
        {
            this.Width = width;
            this.Height = height;

            this.context = new Context(SAMPLE_XML_FILE);
            this.sessionManager = new SessionManager(this.context, "Wave", "RaiseHand");
            this.sessionManager.SessionStart += new EventHandler<PositionEventArgs>(sessionManager_SessionStart);
            this.sessionManager.SessionEnd += new EventHandler(sessionManager_SessionEnd);

            waveDetector = new WaveDetector();
            waveDetector.PointCreate += new EventHandler<HandEventArgs>(waveDetector_PointCreate);
            waveDetector.PointUpdate += new EventHandler<HandEventArgs>(waveDetector_PointUpdate);
            waveDetector.PointDestroy += new EventHandler<IdEventArgs>(waveDetector_PointDestroy);

            this.pointDenoiser = new PointDenoiser(20);
            this.pointDenoiser.AddListener(waveDetector);

            //this.flowRouter = new FlowRouter();
            //this.flowRouter.ActiveListener = this.pointDenoiser;
            this.sessionManager.AddListener(this.pointDenoiser);

            isRunning = true;
            readerThread = new Thread(new ThreadStart(SpinInfinite));
            readerThread.Start();
        }

        private void waveDetector_PointDestroy(object sender, IdEventArgs e)
        {
            if (HandPointDetroy != null)
            {
                this.HandPointDetroy(this, e);
            }
        }

        private void waveDetector_PointUpdate(object sender, HandEventArgs e)
        {
            if (HandPointUpdated != null)
            {
                Point3D newPoint = new Point3D(e.Hand.Position.X, e.Hand.Position.Y, e.Hand.Position.Z);

                // Reset axis
                newPoint.X += this.Width / 2;
                newPoint.Y += this.Height / 2;

                // Max limit
                if (newPoint.X < 0)
                {
                    newPoint.X = 0;
                }
                if (newPoint.Y < 0)
                {
                    newPoint.Y = 0;
                }
                if (newPoint.X > this.Width)
                {
                    newPoint.X = this.Width;
                }
                if (newPoint.Y > this.Height)
                {
                    newPoint.Y = this.Height;
                }
                // Inversion
                newPoint.Y = this.Height - newPoint.Y;

                this.HandPointUpdated(this, new HandPointEventArgs(e.Hand.ID, e.Hand.UserID, newPoint));
            }
        }

        private void waveDetector_PointCreate(object sender, HandEventArgs e)
        {
            if (HandPointCreate != null)
            {
                this.HandPointCreate(this, new HandPointEventArgs(e.Hand.ID, e.Hand.UserID, e.Hand.Position));
            }
        }

        void sessionManager_SessionEnd(object sender, EventArgs e)
        {
            //throw new NotImplementedException();
        }

        void sessionManager_SessionStart(object sender, PositionEventArgs e)
        {
            //throw new NotImplementedException();
        }

        private void SpinInfinite()
        {
            while (this.isRunning)
            {
                this.context.WaitAnyUpdateAll();
                this.sessionManager.Update(this.context);
            }
        }

        public void Dispose()
        {
            //this.flowRouter.ActiveListener = null;
            this.isRunning = false;
            this.readerThread.Join();
        }
    }
}
